import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import gnu.io.CommPortIdentifier; 
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent; 
import gnu.io.SerialPortEventListener; 

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;


public class ArduinoInterface  
implements SerialPortEventListener 
{
 
	
	
	
	SerialPort serialPort;
        /** The port we're normally going to use. */
	private static final String PORT_NAMES[] = { 
			"/dev/tty.usbserial-A9007UX1", // Mac OS X
			"/dev/ttyUSB0", // Linux
			"COM3", // Windows
	};
	/**
	* A BufferedReader which will be fed by a InputStreamReader 
	* converting the bytes into characters 
	* making the displayed results codepage independent
	*/
	private BufferedReader input;
	/** The output stream to the port */
	private BufferedOutputStream output;

	private final int DEFAULT_VEL = 0;
	private final int DEFAULT_DIRECTION = 0;
	private final int DEFAULT_ANGLE = 90;
	
	
	private int vel = DEFAULT_VEL  ;	
	private int beep = 0 ;	
	private int direction = DEFAULT_DIRECTION;	
	private int angle = DEFAULT_ANGLE;
	
	private JSlider slider_angle,slider_vel,slider_direction;
	private JButton button ;
	/** Milliseconds to block while waiting for port open */
	private static final int TIME_OUT = 2000;
	/** Default bits per second for COM port. */
	private static final int DATA_RATE = 9600;

	
	private static ArduinoInterface instance = null;
	
	
	
	
	public static ArduinoInterface getInstance()
	{
		if(instance==null)
			instance=new ArduinoInterface();
		return instance;
	}
	
	
	private ArduinoInterface(){
		CommPortIdentifier portId = null;
		Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

		//First, Find an instance of serial port as set in PORT_NAMES.
		while (portEnum.hasMoreElements()) {
			CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
			for (String portName : PORT_NAMES) {
				if (currPortId.getName().equals(portName)) {
					portId = currPortId;
					break;
				}
			}
		}
		if (portId == null) {
			System.out.println("Could not find COM port.");
			return;
		}

		try {
			// open serial port, and use class name for the appName.
			serialPort = (SerialPort) portId.open(this.getClass().getName(),
					TIME_OUT);
			
			 

			// set port parameters
			serialPort.setSerialPortParams(DATA_RATE,
					SerialPort.DATABITS_8,
					SerialPort.STOPBITS_1,
					SerialPort.PARITY_NONE);

			
			
			// open the streams
			input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
			output =   new BufferedOutputStream(serialPort.getOutputStream());
			

			// add event listeners
			serialPort.addEventListener(this);
			serialPort.notifyOnDataAvailable(true);
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println(e.toString());
		}
		
		gui();
	}

	/**
	 * This should be called when you stop using the port.
	 * This will prevent port locking on platforms like Linux.
	 */
	public synchronized void close() {
		if (serialPort != null) {
			serialPort.removeEventListener();
			serialPort.close();
		}
	}

	/**
	 * Handle an event on the serial port. Read the data and print it.
//	 */
	public synchronized void serialEvent(SerialPortEvent oEvent) {
	
		if (  oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
			try {
				
				String line = null;
				System.out.println("Input "+input);
				 while((line= input.readLine())!=null )
				 {
					 System.out.println(" Arduino > Java : "+line);
				 }				
				System.out.println("=============================\n");
			
//				input.reset();
				
			} catch (Exception e) {
				e.printStackTrace();
				System.err.println(e.toString());
			}
		}
		
		
		
	}
	
	
	
	public  void sendMessage() throws IOException
	{		
//		output = serialPort.getOutputStream();
	
		
//		System.out.println("angle : "+angle);
//		System.out.println("direction : "+direction);
//		System.out.println("vel : "+vel);
//		System.out.println("beep : "+beep);
		
//			System.out.println("DEBUG : Send Message ");
			
		//output.flush();
//			output.write(125);
			output.write(angle);		
			output.write(direction);			
			output.write(vel);	
			output.write(200);
			output.write(beep);
			
			
			output.flush();
			

			
//			System.out.println("DEBUG : Send Message [ OK ] ");
//			output.close();
//			output = serialPort.getOutputStream();
		
			
			
			
			
			
		
	}
	
	
	
	
	@Override
	protected void finalize() throws Throwable {
		System.out.println("Finalize...");
		vel = DEFAULT_VEL;
		sendMessage();
		super.finalize();
	}

	
	public void gui()
	{
		System.out.println("Gui launched!");
		JFrame f = new JFrame();
		 slider_angle = new JSlider();
		 slider_angle.setMinimum(30);
		 slider_angle.setMaximum(150);
		 slider_angle.setValue(90);
		 angle = slider_angle.getValue();
		 
//		 slider_angle.addChangeListener(new ChangeListener() {
//			
//			@Override
//			public void stateChanged(ChangeEvent arg0) {
//				angle = slider_angle.getValue();
//				try {
//					sendMessage();
//				} catch (IOException e) {
//					e.printStackTrace();
//				}
//				
//			}
//		});
		 
		 slider_angle.addMouseListener(new MouseListener() {
				
				@Override
				public void mouseReleased(MouseEvent m) {
					angle = slider_angle.getValue();
					try {
						sendMessage();
					} catch (IOException e) {
						e.printStackTrace();
					}
					
				}@Override
				public void mousePressed(MouseEvent arg0) {
					}
				
				@Override
				public void mouseExited(MouseEvent arg0) {}
				
				@Override
				public void mouseEntered(MouseEvent arg0) {}
				
				@Override
				public void mouseClicked(MouseEvent arg0) {	}
			});
//		 
		 slider_vel = new JSlider();
		 slider_vel.setMinimum(0);
		 slider_vel.setMaximum(255);
		 slider_vel.setValue(0);
		 vel = slider_vel.getValue();
		 
		 slider_vel.addMouseListener(new MouseListener() {
			
			@Override
			public void mouseReleased(MouseEvent m) {
				vel = slider_vel.getValue();
				try {
					sendMessage();
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			}@Override
			public void mousePressed(MouseEvent arg0) {}
			
			@Override
			public void mouseExited(MouseEvent arg0) {}
			
			@Override
			public void mouseEntered(MouseEvent arg0) {}
			
			@Override
			public void mouseClicked(MouseEvent arg0) {}
		});
		 
		 slider_direction = new JSlider();
		 slider_direction.setMinimum(0);
		 slider_direction.setMaximum(1);
		 slider_direction.setValue(0);
		 direction = slider_direction.getValue();
		 slider_direction.addMouseListener(new MouseListener() {
			
			@Override
			public void mouseReleased(MouseEvent arg0) {
				direction = slider_direction.getValue();
				try {
					sendMessage();
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			}			
			@Override
			public void mousePressed(MouseEvent arg0) {}
			
			@Override
			public void mouseExited(MouseEvent arg0) {}
			
			@Override
			public void mouseEntered(MouseEvent arg0) {}
			
			@Override
			public void mouseClicked(MouseEvent arg0) {}
		});
		 
		 
		 
//		 slider_direction.addChangeListener(new ChangeListener() {
//				
//				@Override
//				public void stateChanged(ChangeEvent arg0) {
//					direction = slider_direction.getValue();
//					try {
//						sendMessage();
//					} catch (IOException e) {
//						e.printStackTrace();
//					}
//				}
//			});
		 
		 
		 button= new JButton("Beep");
		button.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				beep = 1;
				try {
					sendMessage();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				beep = 0;
				
			}
		});
		 
		f.setLayout(new GridLayout(4,2));
		f.add(new JLabel("Angle"));
		f.add(slider_angle);
		f.add(new JLabel("Dir"));
		f.add(slider_direction);
		f.add(new JLabel("Vel"));
		f.add(slider_vel);
		f.add(new JLabel());
		f.add(button);
		f.pack();
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	
	
//	@Override
//	protected void finalize() throws Throwable {
//		output.write(0);
//		super.finalize();
//	}
	
//	public static void main(String[] args) throws Exception {
//		ArduinoInterface main = new ArduinoInterface();
//		main.initialize();
//		Thread t=new Thread() {
//			public void run() {
//				//the following line will keep this app alive for 1000 seconds,
//				//waiting for events to occur and responding to them (printing incoming messages to console).
//				try {Thread.sleep(1000000);} catch (InterruptedException ie) {}
//			}
//		};
//		t.start();
//	
//		System.out.println("Started");
//	}
//
//
//
//
//public void sendMessage(boolean red,boolean green,boolean yellow, boolean blue, int rotation) throws IOException
//{
//	String msg = ((red)?1:0)+" "+((green)?1:0)+" "+((yellow)?1:0)+" "+((blue)?1:0)+" "+rotation;
//	output.write(msg.getBytes());
//	output.flush();
//}
	
	
	
}